#include "ConstPropagation.h"

// 给出了返回整型值的常数折叠实现，大家可以参考，在此基础上拓展
// 当然如果同学们有更好的方式，不强求使用下面这种方式
std::map<GlobalVariable*,ConstantInt*> global_int;
std::map<GlobalVariable*, ConstantFloat*> global_float;
ConstantInt *ConstFolder::compute(Instruction::OpID op, ConstantInt *value1, ConstantInt *value2) {
    int const_value1 = value1->get_value();
    int const_value2 = value2->get_value();
    switch (op) {
    case Instruction::add:
        return ConstantInt::get(const_value1 + const_value2, module_);
        break;
    case Instruction::sub:
        return ConstantInt::get(const_value1 - const_value2, module_);
        break;
    case Instruction::mul:
        return ConstantInt::get(const_value1 * const_value2, module_);
        break;
    case Instruction::sdiv:
        return ConstantInt::get((int)(const_value1 / const_value2), module_);
        break;
    case Instruction::srem:
        return ConstantInt::get(const_value1 % const_value2, module_);
        break;
    default:
        return nullptr;
        break;
    }
}

ConstantFloat *ConstFolder::compute(Instruction::OpID op, ConstantFloat *value1, ConstantFloat *value2) {
    float const_value1 = value1->get_value();
    float const_value2 = value2->get_value();
    switch (op) {
    case Instruction::fadd:
        return ConstantFloat::get(const_value1 + const_value2, module_);
        break;
    case Instruction::fsub:
        return ConstantFloat::get(const_value1 - const_value2, module_);
        break;
    case Instruction::fmul:
        return ConstantFloat::get(const_value1 * const_value2, module_);
        break;
    case Instruction::fdiv:
        return ConstantFloat::get((int)(const_value1 / const_value2), module_);
        break;
    default:
        return nullptr;
        break;
    }
}
ConstantInt *ConstFolder::compute(CmpInst::CmpOp op, ConstantFloat *value1, ConstantFloat *value2) {
    float const_value1 = value1->get_value();
    float const_value2 = value2->get_value();
    switch (op)
    {
    case CmpInst::EQ:
        return ConstantInt::get(const_value1 == const_value2, module_);
        break;
    case CmpInst::NE:
        return ConstantInt::get(const_value1 != const_value2, module_);
        break;
    case CmpInst::GT:
        return ConstantInt::get(const_value1 > const_value2, module_);
        break;
    case CmpInst::GE:
        return ConstantInt::get(const_value1 >= const_value2, module_);
        break;
    case CmpInst::LT:
        return ConstantInt::get(const_value1 < const_value2, module_);
        break;
    case CmpInst::LE:
        return ConstantInt::get(const_value1 <= const_value2, module_);
        break;
    default:
        return nullptr;
        break;
    }
}
ConstantInt *ConstFolder::compute(CmpInst::CmpOp op, ConstantInt *value1, ConstantInt *value2) {
    int const_value1 = value1->get_value();
    int const_value2 = value2->get_value();
    switch (op)
    {
    case CmpInst::EQ:
        return ConstantInt::get(const_value1 == const_value2, module_);
        break;
    case CmpInst::NE:
        return ConstantInt::get(const_value1 != const_value2, module_);
        break;
    case CmpInst::GT:
        return ConstantInt::get(const_value1 > const_value2, module_);
        break;
    case CmpInst::GE:
        return ConstantInt::get(const_value1 >= const_value2, module_);
        break;
    case CmpInst::LT:
        return ConstantInt::get(const_value1 < const_value2, module_);
        break;
    case CmpInst::LE:
        return ConstantInt::get(const_value1 <= const_value2, module_);
        break;
    default:
        return nullptr;
        break;
    }
}
ConstantInt *ConstFolder::compute_zext(Instruction::OpID op, ConstantInt *value){
    auto _const = value->get_value();
    if(op == Instruction::zext){
        return ConstantInt::get(_const, module_);
    }
    else return nullptr;
}
// 用来判断value是否为ConstantInt，如果不是则会返回nullptr
ConstantInt *cast_to_const_int(Value *value) {
    auto const_int_ptr = dynamic_cast<ConstantInt *>(value);
    if (const_int_ptr) {
        return const_int_ptr;
    } else {
        return nullptr;
    }
}

ConstantFloat *cast_to_const_float(Value *value) {
    auto const_float_ptr = dynamic_cast<ConstantFloat *>(value);
    if (const_float_ptr) {
        return const_float_ptr;
    } else {
        return nullptr;
    }
}

void ConstPropagation::execute() {
    INT_1=Type::get_int1_type(module);
    INT_32=Type::get_int32_type(module);
    FLOAT=Type::get_float_type(module);
    global_float.clear();
    global_int.clear();
    // set_globalVar();
    for (auto func : module->get_functions()) {
        _func=func;
        for (auto bb : func->get_basic_blocks()) {
            
            Const_Propagation(bb);
        }
    }
    cond_opt();
    delete_trans();
    return ;
}
void ConstPropagation::Const_Propagation(BasicBlock* bb) {
    std::vector<Instruction* >need_del;
    for(auto ins : bb->get_instructions()){
        if(ins->is_cmp()||ins->is_fcmp()){
            auto op = dynamic_cast<CmpInst* >(ins)->get_cmp_op();
            if(ins->get_operand(0) == ins->get_operand(1)){
                if(op==CmpInst::NE||op==CmpInst::GT||op==CmpInst::LT){
                    ins->replace_all_use_with(ConstantInt::get(0, module));
                    need_del.push_back(ins);

                }
                else if(op==CmpInst::EQ||op==CmpInst::GE||op==CmpInst::LE){
                    ins->replace_all_use_with(ConstantInt::get(1,module));
                    need_del.push_back(ins);       

                }
                else{
                    if(ins->is_cmp()){
                        auto value1 = dynamic_cast<ConstantInt*>(ins->get_operand(0));
                        auto value2 = dynamic_cast<ConstantInt*>(ins->get_operand(1));
                        if(value1 && value2){
                            auto _const = folder->compute(op,value1,value2);
                            ins->replace_all_use_with(_const);
                            need_del.push_back(ins);
                        }
                        
                    }
                    else{
                        auto value1 = dynamic_cast<ConstantFloat*>(ins->get_operand(0));
                        auto value2 = dynamic_cast<ConstantFloat*>(ins->get_operand(1));
                        if(value1 && value2){
                            auto _const = folder->compute(op,value1,value2);
                            ins->replace_all_use_with(_const);
                            need_del.push_back(ins);
                        }
                    }
                }
                // else{
                //     auto value1 = dynamic_cast<ConstantInt*>(instr->get_operand(0));
                //     auto value2 = dynamic_cast<ConstantInt*>(instr->get_operand(1));
                //     if(value1 && value2){
                //         auto _const = folder->compute(op,cast_to_const_float(value1),cast_to_const_float(value2));
                //         ins->replace_all_use_with(_const);
                //         need_del.push_back(ins);
                //     }
                //     else if(value1 == nullptr){
                //         value1 = dynamic_cast<ConstantFloat*>(ins->get_operand(0));
                //         if(value2){
                //             auto _const = folder->compute(op,value1,cast_to_const_float(value2));
                //             ins->replace_all_use_with(_const);
                //             need_del.push_back(ins);
                //         }
                //         else{
                //             value2 = dynamic_cast<ConstantFloat*>(ins->get_operand(1));
                //             auto _const = folder->compute(op, value1, value2);
                //             ins->replace_all_use_with(_const);
                //             need_del.push_back(ins);
                //         }
                //     }
                //     else {
                //         auto value1 = dynamic_cast<ConstantFloat*>(instr->get_operand(0));
                //         auto value2 = dynamic_cast<ConstantFloat*>(instr->get_operand(1));
                //         auto _const = folder->compute(op , value1, value2);
                //         ins->replace_all_use_with(ins);
                //         need_del.push_back(ins);
                //     }

                // }
            }

        }
        else if(ins->is_binary()){
            auto op = ins->get_instr_type();
            if(ins->is_int_binary()){
                auto value1 = dynamic_cast<ConstantInt *>(ins->get_operand(0));
                auto value2 = dynamic_cast<ConstantInt *>(ins->get_operand(1));
                if(value1 && value2){
                    auto _const = folder->compute(op, value1, value2);
                    ins->replace_all_use_with(_const);
                    need_del.push_back(ins);
                }
            }
            else{
                auto value1 = dynamic_cast<ConstantFloat *>(ins->get_operand(0));
                auto value2 = dynamic_cast<ConstantFloat *>(ins->get_operand(1));
                if(value1 && value2){
                    auto _const = folder->compute(op, value1, value2);
                    ins->replace_all_use_with(_const);
                    need_del.push_back(ins);
                } 
            }
        }
        else if(ins->is_call()){
            global_int.clear();
            global_float.clear();
        }
        // else if(ins->is_load()){
        //     auto load_inst=dynamic_cast<LoadInst*>(ins);
        //     auto lval=load_inst->get_lval();
        //     auto global_var=dynamic_cast<GlobalVariable*>(lval);
        //     if(global_var&&global_var->get_type()->is_pointer_type()){
        //         auto origin_var=global_var->get_init();
        //         auto int_var=dynamic_cast<ConstantInt*>(origin_var);
        //         std::cout<<1<<std::endl;
        //         if(int_var){
        //             if(global_int.find(global_var)!=global_int.end()){
        //                 std::cout<<2<<std::endl;
        //                 auto replace_var=global_int[global_var];
        //                 std::cout<<3<<std::endl;
        //                 if(replace_var==nullptr) std::cout<<"null"<<std::endl;
        //                 std::cout<<replace_var->get_value()<<std::endl;
        //                 ins->replace_all_use_with(replace_var);
        //                 std::cout<<4<<std::endl;
        //             }
        //         }
        //         else{std::cout<<"cgg"<<std::endl;
        //             auto float_var=dynamic_cast<ConstantFloat*>(origin_var);
        //             if(float_var){
        //                 std::cout<<"cgg2"<<std::endl;
        //                 if(global_float.find(global_var)!=global_float.end()){
                            
        //                     auto replace_var=global_float[global_var];
        //                     if(replace_var==nullptr){
        //                         std::cout<<"null"<<std::endl;
        //                     }
        //                     ins->replace_all_use_with(replace_var);
        //                     std::cout<<"cgg3"<<std::endl;
        //                 }
        //             }
        //         }
        //     }
        // }
        // else if(ins->is_store()){
        //     auto store_inst=dynamic_cast<StoreInst*>(ins);
        //     auto lval=store_inst->get_lval();
        //     auto update_val=dynamic_cast<ConstantInt*>(store_inst->get_rval());
        //     //if(update_val==nullptr) std::cout<<"update failed"<<std::endl;
        //     auto global_var=dynamic_cast<GlobalVariable*>(lval);
        //     if(global_var==nullptr||!global_var->get_type()->is_pointer_type()) continue;//如果不是全局变量的非数组情况就跳出
        //     if(update_val){
        //         if(global_int.find(global_var)!=global_int.end()){
        //            // std::cout<<"update failed1"<<std::endl;
        //             global_int[global_var]=update_val;
        //         }
        //         else{
        //             //std::cout<<"update failed3"<<std::endl;
        //             global_int.insert(std::pair<GlobalVariable*,ConstantInt*>(global_var,update_val));
        //         }
        //     }else{
        //         auto update_val=dynamic_cast<ConstantFloat*>(store_inst->get_rval());
        //         if(update_val){
        //             if(global_float.find(global_var)!=global_float.end()){
        //                 global_float[global_var]=update_val;
        //             }
        //             else{
        //                 global_float.insert(std::pair<GlobalVariable*,ConstantFloat*>(global_var,update_val));
        //             }
        //         }
        //     }
        // }

    }
    

}
void ConstPropagation::cond_opt(){
   for(auto func:module->get_functions()){
        for(auto bb:func->get_basic_blocks()){
            builder->set_insert_point(bb);
            auto br_inst=bb->get_terminator();
            if(!br_inst->is_br()) continue;
            auto cond_br=dynamic_cast<BranchInst*>(br_inst);
            if(cond_br&&cond_br->is_cond_br()){
                auto cond_inst=dynamic_cast<ConstantInt*>(cond_br->get_operand(0));
                auto truebb=cond_br->get_operand(1);
                auto falsebb=cond_br->get_operand(2);
                BasicBlock* nextbb;
                BasicBlock* deletebb;
                if(cond_inst){
                    if(cond_inst->get_value()==1){
                        nextbb=dynamic_cast<BasicBlock*>(truebb);
                        deletebb=dynamic_cast<BasicBlock*>(falsebb);
                    }
                    else{
                        deletebb=dynamic_cast<BasicBlock*>(truebb);
                        nextbb=dynamic_cast<BasicBlock*>(falsebb);
                    }
                    for(auto succ_bb:bb->get_succ_basic_blocks()){
                    succ_bb->remove_pre_basic_block(bb);
                    }
                    builder->create_br(nextbb);
                    bb->delete_instr(br_inst);
                    bb->remove_succ_basic_block(deletebb);
                }
            }
        }
    }
}
void ConstPropagation::set_globalVar(){

    auto global_list=module->get_global_variable();
    std::cout<<global_list.size()<<std::endl;
    for(auto var:global_list){
        std::cout<<var->get_name()<<std::endl;
      auto origin_val = var->get_init();
      auto value = dynamic_cast<ConstantInt *>(origin_val);
      if (value) {
        std::cout << "insert" << std::endl;
        if (value == nullptr)
          std::cout << "null" << std::endl;
        global_int.insert(std::pair<GlobalVariable *, ConstantInt *>(var, value));

      } 
      else {
        auto value = dynamic_cast<ConstantFloat *>(origin_val);
        if (value == nullptr)
          continue;
        global_float.insert(std::pair<GlobalVariable *, ConstantFloat *>(var, value));
      }
    }
}



void ConstPropagation::delete_trans(){
    for(auto bb:_func->get_basic_blocks()){
        auto inst_list=bb->get_instructions();
        for(auto curinst:inst_list){
            if(curinst->is_zext()){
                if(curinst->get_operand(0)->get_type()==INT_32){
                    curinst->replace_all_use_with(curinst->get_operand(0));
                    bb->delete_instr(curinst);
                }
            }else if(curinst->is_sitofp()){
                if(curinst->get_operand(0)->get_type()==FLOAT){
                    curinst->replace_all_use_with(curinst->get_operand(0));
                    bb->delete_instr(curinst);
                }
            }else if(curinst->is_fptosi()){
                if(curinst->get_operand(0)->get_type()==INT_32){
                    curinst->replace_all_use_with(curinst->get_operand(0));
                    bb->delete_instr(curinst);
                }
            }
        }
    }
}